Welcome back to part two of scripting our AI.
We're going to jump right back into where we left off, which was determining what to do when our AI
gets a target or gets no target at all.
So if our AI does sense a target and it's visible and the AI can see them and they're the closest target,
then we actually need to go ahead and have the AI path find or start chasing that particular player.
So what we could do is we could create a function and we could call it something like New Path two,
and we would pass the target that we need to go ahead and path find towards.
So we would pass our target and this function would basically handle the rest for us.
So we could go ahead and create this new function, call it new path two.
We get a target and this is where we would start either pathfinding towards that target or directly
chasing after that target if we can see them clearly, and before we also start chasing or creating
a path towards this target, we also want to go ahead and update the walk speed of our humanoid.
So my humanoid walk speed and set it equal to the AI properties dot chase speed.
And just to make this autofill a little bit easier, I'm actually going to go ahead and take our handsome
Squidward AI and put him back inside of the other stuff folder inside of server storage.
That way we can actually refer to him properly.
But after we set them to the chase speed, we need to start pathfinding to our target.
So to Pathfind to our target.
This is where we're going to have to use our pathfinding service.
And there's a function in there called Create Path.
This is where we basically create a new path instance where we can go ahead and compute waypoints based
on two different positions.
And this function has to get past a table called agent Parameters.
And this lets you fine tune the path for the size of the agent, or aka the humanoid, or our AI that's
going to be pathfinding along this path.
So that means inside of our properties module script, we're going to have to create a new section in
here to define all of the different agent parameters for pathfinding.
So what I'm going to do is down here, I'm going to create a key.
I'm going to call it Agent Parameters.
And we'll set it equal to a table.
Instead of this table, we're going to have to define a bunch of different key value pairs.
One of those is going to be called agent Radius.
And we're going to set this at two studs.
What does this represent.
Well this basically represents the width of our AI or our character right.
How wide is our character.
And this helps to determine basically the different areas that our AI can pathfind.
Because let's say there's a hole in the map, it's tall enough, but it's not wide enough.
Well, we can use the agent radius to determine the minimum radius between two parts that our AI can
pathfind to.
So since the width of our AI is about four studs, then that means our AI has to find a path that has
a minimum width of four studs to be able to pathfind through it.
Another one is called agent height, which determines the height or the minimum height that the AI has
to pathfind through.
And since our AI is about six studs tall, we're going to set this to six.
Another thing we need to determine is whether or not we can jump.
So we can call this agent can jump.
And we're going to set this to true.
And this is just in case for some reason when your AI is pathfinding, it needs to be able to jump to
get to a particular area.
Another one is called Agent Can Climb.
Now, I don't anticipate our AI being able to climb anything because there's nothing to climb in the
basement, so we're just going to set this to false.
And another thing we need to determine is something called waypoint spacing.
And this determines basically how far apart should each of the waypoints be when it calculates between
two different positions.
I found four studs to be pretty good, but if you want your, um, path to be more detailed, then you
would reduce this number to something like one.
So each time it would move one stud and your path would be super detailed.
Four studs I found to be pretty good.
The last thing we want to define inside of the agent parameters is a table called costs.
And this table basically defines, um, a bunch of different materials and their costs to traverse over
them.
So basically you could use this table as a custom way of making your AI avoid particular materials.
So for example you would like the AI to walk over wood more than it would rock.
So you would define in here, um, that material and the cost to walk over it.
We don't really care about that.
So we're just going to leave this blank.
But now that we've created this agent parameters, what we can do is we can go back and we can pass
that agent parameters to our create path function.
So we can refer to AI properties and pass agent parameters.
And this will go ahead and return back to us a path instance.
And using this path instance what we can do is we can use a function in there called compute async.
So actually if I denote this here as a path and then I can go ahead and try again.
There we go.
We get our compute async function.
We get a start vector three and a finishing vector three.
So our start vector three is going to be my route part dot position.
And the place we want to path find towards is going to be our target dot position.
So this is going to sit here and compute the path and try to figure out the best way to get from this
position to this position, and then it'll tell you whether or not it was successful in creating this
path.
So if the path dot status.
Is not equal to the enum dot path status dot success.
So if we were not successful in pathfinding towards this player, then we've run into a problem.
Either our AI could possibly be stuck, or there's something else weird going on.
So just in case, for some reason our AI is stuck, what we could do is we could refer to my humanoid
and set the jump property equal to true to make our AI jump, we'll wait something like 0.1 seconds,
and then we're just going to return out of this function and try to find a different player to pathfind
towards.
Otherwise.
If we were successful and calculating a path, then what we could do is we could get all the waypoints
for this path, which is equal to path, get waypoints.
And what we're going to do is we're going to basically loop through every single waypoint inside of
this table and path, find towards that particular waypoint.
The first thing we want to check is the action for this waypoint.
So for example, if this waypoint is at a higher position in our AI needs to jump for that, well we
can go ahead and check.
So if this waypoint.
Dot action.
And actually we can go ahead and denote this as a path waypoint.
So if this waypoint dot action, the action to perform with this waypoint is equal to the enum dot path
waypoint action dot jump.
So if we need to jump to get to this waypoint, then we can refer to my humanoid and set jump equal
to true.
And then from this point we're going to refer to my humanoid again and use the move two function.
And we want to move to this waypoints position.
So waypoint dot position.
And then from this point, we can use our wait for event function and wait for our my humanoid dot move
to finish.
So that way we actually know that we successfully moved to this waypoint.
And then of course we're going to have a timeout.
So if for some reason we timed out, then again it could mean that our AI is stuck.
So we're going to check if we timed out.
If that's the case, then we can print out a warning and say something like our squid word.
I, uh, timed out when path finding to player.
Is it stuck?
We could say something like that.
And we're going to refer to my humanoid set jump equal to true to try to get our AI unstuck.
And then we're going to break basically out of this for loop.
Another thing we want to check is whether or not our AI can directly see our player.
So if not, check visibility and we pass, um, the target.
So if we cannot see this player, then we're just going to continue looping.
Or basically we're just going to continue pathfinding along these waypoints.
If we cannot see the player.
However, if we can see the player, then there's no need to pathfind.
And instead what we could do is run directly towards our player.
So what we're going to do is we're going to first get this player by referring to the player service
and use the function get player from character and pass our target dot parent.
Now we also want to go ahead and create a variable to refer to the last position of the player, just
in case the player gets out of our sight.
So if the player, you know moves around a corner and disappears out of our sight, then we can have
the AI run or walk to the last position that they were seen at.
So we'll refer to the target dot position.
And basically as long as we can see this player, then we can just run directly towards them.
So we're going to have a repeat loop here.
And now we're going to do is update our last position equal to the target dot position.
And we're going to use my humanoid and use the move two function and pass the last position.
So we're going to have our humanoid constantly running towards our player.
And at the same time we're also going to try and attack our player as well.
So we can have a function called attack and pass our target to it to try to get our AI to attack or
jump scare our player.
So we could create another function up here called attack, which will get our target, and we'll fill
this out.
Just a moment.
But after that, to what we could do is we could check, um, let's say our player dies or for some
reason, our player's character gets destroyed.
So if target dot parent is equal to nil for some reason, which means they got destroyed, then we're
just going to break out of this repeat loop.
Otherwise we're just going to yield for point let's say zero five seconds.
So we're going to continue running towards our player.
And we're going to keep trying to attack them until either they are no longer visible or we are no longer
active, or the player is dead or they're inside of a locker or whatever.
So until not, let's say check visibility of this target.
So if we can no longer see them, then we want to break out of this repeat loop.
Or if we are no longer active or our AI has been stopped, or let's say our target dot parent dot humanoid
dot health becomes less than one, which means the player died.
Then we don't want to, you know, continue chasing after them.
Um, another thing we could check is, let's say the player get attribute inside locker is true.
So let's say they hop inside of a locker while we're tracing them.
Well, we don't want to chase them anymore.
Or we can check if the player get attribute.
Let's say they have escaped.
So escaped.
So basically, after we have lost sight of this player or the player has gone inside of the locker,
then what we want to do is we want to either move our humanoid to the last position that our player
was seen at, or we want to move our AI to a new player that is visible.
So, for example, let's say right when we lose visibility of this player, we can actually see another
player.
We want to go ahead and start chasing that player instead.
So what we could do is we could check if there is a nearest visible player.
So if get nearest visible player returns something back to us, then we're just going to completely
break out of this for loop, which allows us to exit out of our new path to function, and which we'll
go back into our while loop and it'll get us a new target, and then we start chasing that player instead.
So we break out if we see another player, but if we don't see.
Another player, then what we could do is just refer to our humanoid and move him to the last position
that we saw this player at, and we'll just wait for this event.
So my humanoid move to finished.
And we can have a timeout of like three seconds.
So this function right here should allow us to either pathfind towards players after we create some
waypoints to them, or if we see that player directly, then we can just repeatedly run towards that
player and try to attack them until we can no longer see them or they're inside of a locker or whatever.
So actually what we could do is go ahead and fill out our attack function.
We can go ahead and scroll up and inside of our attack function, the first thing we want to make sure
is that we can actually attack.
So if we cannot attack, then we're just going to return out of this function.
Another thing we want to check is whether or not this target is actually a player.
So we're going to use players.
Get player from character and past.
Target.
Parent.
So for some reason this is not a player.
So if not player then we're just going to return as well.
Another thing we want to check is if this player is inside of a locker or they have escaped, or let's
say they've already been jumpscared.
Then we want to ignore this player.
So if player get attribute inside locker or player get attribute escaped, or what we could do is we
could set another attribute on this player.
So if player get attribute we could call it something like jumpscare.
So if that's true then we're just going to return as well.
So when we jumpscare our players we're going to set an attribute on them of jumpscare.
And we can set that equal to true to signify that this player has already been jumpscared.
Another thing we want to check is if this player team for some reason is on the, uh, dead team.
So if this player is dead, then we don't care about them either.
We're going to return.
Um, another thing we want to check is whether or not this player is within, like, an attack range.
So we can go ahead and define that as well within our properties table.
So up here we can go up and we can create something like uh we could call it attack range.
So what we'll do here.
Here's I'll create something called attack range.
And that's going to be equal to we could do something like within four studs.
So if a player is within four studs of our AI then they can get jump scared.
So we'll go back and we'll check if my root part dot position subtracted by the target dot position
get the magnitude of that.
If that's greater than I properties dot attack range, then we're just going to ignore this player.
And another thing we want to check is if the target parent for some reason is equal to nil, which means
somehow this was destroyed.
So we're going to return as well.
Basically, these are just a whole bunch of different fail safes just to make sure we don't get any
errors.
Otherwise if everything checks out then we can set Kanatak equal to false.
And we're going to set on this player the attribute of jump scare and set that equal to true.
Then we can use our manipulate camera, event and fire to this particular player that we want them to
jump scare.
So we could do player camera manipulation.
Enum dot two client dot jump scare.
And then from this point, uh, we'll just move the player C frame back inside of, like, the spawn.
So we could do player dot character dot humanoid route part.
Get the keyframe and set that equal to inside of the workspace.
Inside of the filtering folder, there's a part in there called teleport Part and spawn, and we'll
just set the player's, uh, keyframe equal to this keyframe, and then we'll set the team of our player
equal to the dead team, because, I mean, they're getting jumpscares, they're dead.
And then after this, we'll have a function be delayed for two seconds, because what we're going to
do is we're going to use our update guy event and fire to this particular player to tell them that they
died.
So we don't want to tell them that they died immediately, because that will fade the death screen on
their screen right away.
But instead we want to wait two seconds.
That way the player actually has enough time to display the jumpscare on their screen.
That way they get spooked, and then after two seconds, we're going to tell this player, uh, that
they died so we could do GUI actions.
Enum Dot died and then we'll set on this player jumpscare back to false.
That way they can be jumpscared again in the future in case the player decides to respawn back into
the map.
And then finally we can have another delayed function to go ahead and reset the can attack variable
back to true.
So that means we can have another property inside of here.
We could call it like cooldown between attack, and I'll just set it to something like, uh, I don't
know, 1.5 seconds.
That's probably good.
So we'll delay a function from running for our AI properties dot cooldown between attacks.
So after 1.5 seconds is up, then we can go ahead and set can attack back equal to true.
That way our AI is able to attack another player again.
All right.
Perfect.
So everything's looking pretty good here so far.
I think the next thing we can go ahead and fill out is the functionality to fulfill when we actually
get told to be jumpscared.
So on the client and our camera manipulation handler.
When we get given the action of jump scare.
Currently we have nothing inside of here.
But my plan is is we're going to use our jump scares module script, and we can have a section in here
that stores a bunch of different functions for jump scaring the player, and we can just pick one of
those functions randomly if you would like.
You could have multiple different types of jump scares and pick one randomly and the jump scare the
player with that.
So inside of our jump scares module script, we could create a key.
I'll call it all jump scares and it could be equal to a table.
And inside of here you could just store a whole bunch of different functions.
I'll just create a lambda function in here.
And all this is going to do is basically just, um, refer to our jump scare model that is inside of
replicated storage.
Set that in the map, we would move the player's camera onto the part that's inside of that jump scare
model, jump scare the player and then you know that's that.
So inside of here, what we could do is refer to a few services.
One we'll need is replicated storage.
And we're also going to need the sound service.
In this point, we can go ahead and make a quick reference to the current camera.
So that's in workspace dot current camera.
And we could also go ahead and make a reference to the jump scare model which is in replicated storage
instances.
And we get the jump scare model inside of there.
From this point.
When this function gets called, what we could do is we would first get the original field of view of
our camera.
So current cam field of view, because I kind of want to shrink the field of view to make it zoomed
in on, uh, our squidward's face.
But basically it will refer to our jump scare model, set the parent of that equal to the workspace,
and then refer to our current camera and set the camera type equal to enum dot camera type dot scriptable.
And then set the current camera field of view, and I found a value of 40 to be pretty good.
And then we're going to refer to our current camera's C frame and update that equal to the jump scare
model.
And inside of there there's a camera part.
And we want to set our player C frame equal to that part.
So in fact, if we actually.
Let me close out all this and go into replicated storage.
We have a jump scare model inside of the instances folder.
We put that in the workspace and go to that.
Uh, here's our jump scare model.
We have an invisible part right here, which is our camera part that we're going to set the player's
camera, see frame to, to look directly into the face of our Squidward.
And then from this point, we can play some wacky animation and play a loud sound to jump scare our
player.
So let me go ahead and move this guy back inside of the instances folder.
Because what we can do from this point is we can get the animation track from our jump scare model.
So remember when we originally set up our game, you had that animation inside of the jump scare model
that you set up for the AI squid, where that's in there.
Um, if you haven't done that yet, then go ahead and do that real quick.
Just open up your animation editor and edit the animation for your squid word.
Create some kind of neat little custom jump scare animation.
I've already created one and put my ID in there, so I'm going to get the track by referring to the
jump scare model.
Dot.
Get the Squidward that's in there, get his humanoid and get the animator, and we're going to load
the animation jumpscare model dot Squidward dot jumpscare animations and get jumpscare number one.
And then we can play this track and at the same time, inside of the sound service SFX, there's a folder
called jumpscare, and there's a sound in there called jumpscare one, and we're going to play that
sound, and I'll demonstrate that sound real quick for you.
So we go to the sound service SFX jumpscare jumpscare one we play this.
It's very, very loud and it'll surely knock the socks off of anyone who hears it.
So after that plays, what we're going to do is we're going to wait for that sound to finish playing.
And after that is done, then what we could do is we could set the current camera field of view back
to the original field of view.
And we're also going to stop the animation track from playing and put our jump scare model parent back
into replicated storage instances.
And the reason we're not resetting our camera from here is because when our player dies, then when
we start to enable the spectating system, that spectating system is going to handle, resetting the
player's camera and placing it on the map onto whatever players are left that are alive and stuff like
that.
So we don't have to worry about controlling or resetting the camera from here.
But here is our very basic jump scare function.
And from this point, what we could do inside of our camera manipulation handler is we've already,
I believe.
Yeah, we've already required our jump scare module.
So what we could do from this point is refer to our jump scare module.
And get the all jump scares table.
And inside of here we can pick out a random jump scare and we'll use RNG next integer.
And pick a value starting at one to the number of functions that are inside of our all jumpscares table.
And then to actually call this function, we're just going to put the two parentheses at the very end,
which allows us to execute the functionality for our jumpscare.
And that's it.
So now any time we get told to be jumpscared, we should get this jumpscare played on our screen.
Perfect.
All right, so back inside of our AI squid order handler, the next thing we can go ahead and figure
out is what we want to do when we do not have a target.
Well, what we want to do is we want to wander around the map, right?
But we only want to wander around the map if we haven't exceeded those wandering iterations I was talking
about.
So we want to keep track of how many times our AI has been wandering around the map to different points.
And if our AI has wandered around the map to too many different points, then we want to respawn our
Squidward.
So when we do get a target, we could reset our wandering iterations back to zero.
Otherwise, what we want to do is we want to check if our wandering iterations is below a particular
threshold.
So again, inside of our properties table we could create a new value in here.
We could call it something like max wandering iterations.
And we could set this to something like uh, maybe five.
I think five would be good.
Of course you can adjust this value to whatever you like.
So far, wandering iterations becomes greater than or equal to our AI properties.
Max wandering iterations.
Then that means we have not seen any other players because our wandering iterations hasn't been reset
and we've been wandering around the map for way too long.
So what we could do is we could set wandering iterations back to zero and use our disappear and spawn
a new location function to have our AI, you know, disappear and spawn at a new location to try to
find new players to attack otherwise.
If we don't have a target and we haven't exceeded the wandering iterations threshold, then we can go
ahead and just randomly wander around the map.
So the first thing we would want to do is we would want to refer to my humanoid walk speed and set that
equal to the AI properties dot wander speed.
So that way our AI is just casually wandering around the map, and we would want to set wandering iterations
plus equal one, and we would want to create a function we could call it like wander to have our, uh,
you know, I wander from point to point on our map.
So let's actually go ahead and create that real quick.
We can create a function, we'll call it wander.
And inside of here.
What we would want to do is we would want to create a variable called goal.
And this would represent the part on the map that we would want to pathfind towards.
So that means we would have to create a function to basically get the nearest waypoint to our Squidward.
So that means we'll create another function and we can call this function get nearest waypoint.
And we could have this return back to us.
A part that is the closest to our Squidward that we need to wander to.
So we could call this get nearest waypoint.
And since this would return back to us a part, we could get the position of that part and store it
within our goal variable here.
So what we want to do is instead of get nearest waypoint, we want to make sure we give our eye Squidward,
a waypoint that they haven't passed around to yet.
So the first thing I want to verify is if the number of elements in our points already visited table
is equal to the number of walk to points that we have on the map.
So this signifies that our eye Squidward has already visited or path found to every single point on
the map.
If that's the case, then we need to reset our points already visited table by clearing out all the
values inside of there.
Otherwise, we're basically going to do the same thing that we did for the Get nearest visible player.
We'll have a variable, we'll call it closest Point.
We're not going to initialize it to anything just yet.
And we're also going to have a last distance to calculate or basically compare, you know, the last
distance to the closest point.
And we're going to just set this to some randomly high value.
And then we're going to loop through every single point and walk two points.
And what we could do is we first want to make sure that we haven't visited this point.
So if we find inside of our points already visited table this particular point, then we're just going
to continue looping.
Otherwise, we're going to calculate the distance from my root part dot position.
Subtract this by this point's position.
And get the magnitude of that.
If this distance is, let's say, less than the last distance that we created up here, then we can
go ahead and set closest point equal to this new point, and then set last distance equal to the distance
we just calculated.
So this way we are looping through all the points and going through the process of elimination to find
the absolute closest point.
And then once we do that we can go ahead and return this closest point.
And we could also insert this point into our points already visited table to signify that this point
we have already passed found towards.
So from this point we get the goal that we want to pathfind towards, and then we're going to use the
path find service again to get a path.
So we'll use pathfinding create path.
Again we're going to pass the eye properties dot agent parameters.
And then we're going to use the path to compute starting at my root part dot position to this new goal
that we just got from our get nearest waypoint function.
Now if the path dot status is not equal to the enum dot path status dot success, then we need to print
out again or put a warning that our Squidward I was unable to calculate wander path to this particular
point.
Right from that point, what we would do is we could clear out all of the points inside of our points
already visited table.
We'll set wandering iterations to zero, and what we could do is just disappear and spawn at a new location
and try again, and then we'll just return out of this function.
Otherwise.
If we were successful, then again, we're going to get all of the waypoints using path.
Get waypoints.
And we're going to loop through every single waypoint.
So that's a path a waypoint inside of our waypoints table.
And all we're going to do is we're going to first check if the waypoint dot action is equal to the enum
dot path waypoint action dot jump.
If it is then we're going to have our humanoid jump.
Otherwise, we're going to do my humanoid move two and move him to this waypoint position.
An accident would capitalize that.
My bad.
And then again, we're going to use our weight for event function and weight for the My Humanoid dot
move to finish and have a timeout of like three seconds.
And we can check if we timed out.
So if we timed out, then we can print something like, uh, Squidward, I.
Timed out during a wander.
Something like that.
We'll do my humanoid jump equal to true just in case.
For some reason, our AI is stuck, and then we're just going to break out of our for loop.
Otherwise, another thing we want to check is while we are wandering around the map, is whether or
not we have a visible player.
So we're going to get a target and we're going to do get nearest visible player.
And if we have a target, then we want to return that target from this function back to our main function.
So we could get a variable.
We'll call it target scene while wandering.
So this will either be nil or will actually get returned back to us.
A target to chase after.
So if we see a target while we are wandering around the map, then we're going to return that target.
Otherwise, if we break out of this for loop or we finished pathfinding to this particular goal point
here without seeing any players, then what I want to do is I want to go ahead and like wait for a little
bit.
I just want to have our AI stand still and not instantly pathfind to the next waypoint.
That way, you know, um, he's not constantly moving all the time and it looks a little bit more natural.
What I could do is I could just have a wait, uh, statement here and just wait a random amount of time.
But the problem with doing this is that if a player somehow were to walk in front of our AI while they
were wandering and this wait statement is going on, our AI is going to be stuck just sitting there
doing nothing.
So to avoid that, what we could do is we could have a for loop and we could start it at an index of
one and use RNG next integer and pick a random number between like four and 16.
Because what we're going to do is we're going to yield for like 0.25 seconds.
So that way every 0.25 seconds our AI will actually check if there's any players in front of it.
So what we could do is we could check to see if we have a target.
And actually we'll just copy exactly what we did right here.
So we'll just sit here and yield, have our AI stand still for a little bit.
And while our AI is standing still every 0.25 seconds, it's going to check whether or not there is
a nearest visible player.
If it is, it's going to return that target back out of our wander function, and then we can get that
target.
And what we could do is we could check if there was a target seen while wandering.
If that's the case, then we're just basically going to do the exact same thing that we did up here.
We're just going to copy this, paste that in here and we're going to pass target seen a while wandering.
Now the last important thing that we need to do in this while loop is we need to yield.
Okay.
We don't want to be flying through this while loop.
So we want to, uh, basically have a yield statement down here.
And we could do something like 0.1 seconds.
So every 0.1 seconds this is looping running through the different functions, checking to see if there's
any nearest visible players.
If there's a target um it'll check the wandering iterations to make sure that we're not wandering too
much.
And if we are, we'll disappear and spawn at a new location.
It'll check to see if we have a target while we're wandering, or if we just see a target.
Then we'll just chase directly after them and try to attack and jump scare them all that kind of good
stuff.
All right?
Otherwise, uh, another few things we can go ahead and fill out real quick is our stop AI function.
This one is going to be really easy.
All we're going to do is just set active equal to false when we want to stop our AI.
And when we do stop our AI, we could put down a warning message down here like, um, we could say
something like AI is no longer active.
And another thing I actually want to check real quick is that when we are chasing after a player, let
me figure this out real quick.
Here we go.
When we are chasing after a player, we want to make sure that this player that we are chasing after
and trying to attack hasn't already been jumpscared.
So inside of this until section, we'll also check if this player has the attribute Jumpscared.
So I don't remember if it was jump scare or jump scare.
It was just jump scare.
Okay.
So if this player was inside of a locker they escaped or they've been jump scared or we don't see them
anymore or they died, whatever, we stop chasing after them.
That seems pretty good.
And guess what?
I think we're actually done with our I, Squidward handler.
So because we are calling the start, I function within our game section's module script here.
So after the basement stuff sets up, we start the I, which means we spawn this function right here
in a new thread.
And that means our main function here gets called, which starts looping and starts up our AI to start
wandering around the map.
So the only thing left to do at this point is to test whether or not our AI is actually working.
So let's go ahead and find out.
So after this cutscene, hopefully our AI Squidward, should start to set up and wander around the map.
And unfortunately, we did get an error in the console load animation requires the asset ID not to not
be empty.
Oh, it looks like I accidentally left an asset or an ID in there empty.
I do see glowing eyes back there.
So what does our Squidward doing?
Fortunately, our scooter doesn't look like to be doing much.
That's a little interesting.
I guess we'll have to go ahead and figure out, uh, why our Squidward isn't working with this error
here in particular.
It's just because I accidentally left an animation ID inside of my Squidward empty.
Which was actually, if I go to server storage, we go down to my handsome Squidward.
I inside of animate.
I believe my fall animation.
Yeah, there's nothing in my fall animation, so I'll just input some random number in there.
It doesn't really matter just to make sure that this doesn't get upset, but it looks like we didn't
get any other errors.
It's just that our I, Squidward, was not wandering around the map, so.
Let's go ahead and figure out exactly why that was the case.
All right.
So after a long time of testing and debugging and trying to figure out why our AI wasn't working out
of one of my test iterations, I actually finally got an error message in the console that I made a
typo and the typos in the get nearest visible player function where I spelt character incorrectly,
which is kind of interesting because it didn't show this error until later.
We didn't get this error message the first time, even though this little spelling mistake was the root
of the entire reason for why our AI was not working, this simple little typo, which for some reason
did not appear in the console.
For some reason, I didn't get any error messages until randomly later, which is really weird.
I'm assuming that's some kind of studio bug or something.
But anyways, let's go ahead and fix this typo here to make sure it says character, not character,
and let's go ahead and test out our AI again and see if it actually works this time.
All right we're heading into the basement.
Let's go ahead and hope this time that our AI is actually functioning.
So go ahead and grab my flashlight off the floor, and let's go ahead and see if we can find our little
eye friend wherever he's wandering about.
The most important part is to listen for footsteps.
Oh, there he is.
And there we go.
He's chasing right after us.
Um, that is a little strange.
Why did he run away and not exactly sure?
Well, we can go ahead and test to see if the jump scare works.
Hey, at least the jumpscare works.
That's nice.
But, uh, I think we need to figure out why exactly he ran away from us.
So let's go ahead and figure that out real quick.
Okay, so back into our new path to function, which is responsible for chasing after the player.
As we saw, the AI was pathfinding around the map and eventually after it saw us, it began executing
this repeat loop where he was continually moving towards us until we were no longer visible.
So if you remember, we went around that corner and then all of a sudden he ran away from us.
So we know that after this repeat loop is completed and we disappeared around that corner and he began
to run away from us.
Our issue must be down here somewhere.
So first off, we check if we can get the nearest visible player.
And since we disappeared around the corner, that's not true.
So what we have them do is we have a move to the last position we saw, which is great.
And then we wait for that to finish.
Now, I think our issue here is that because we ran so far away from his pathfinding system, after
this wait event is completed, this goes through the next iteration of a loop and moves to the next
waypoint, which makes sense why?
After this completed and he moved to our last position, he began to sprint away from us back to a different
waypoint position because we're continuing to loop through this for loop.
So a way that we could fix that is we could just simply insert a break statement right here.
So after he moves to the last position, we completely break out of this for loop and exit out of our
new path to function, which allows our Squidward to enter back into this while loop and then begin
to look for another nearest visible player.
So now everything should be working, and we could go ahead and test this out one more time.
All right, we're heading back into the basement.
Hopefully our AI, Squidward, should be working just fine this time.
So let's go ahead and get our flashlight and see if we can find him somewhere.
So the first important thing is to listen for his footsteps.
So take a look around.
And there he is.
So if I disappear around this corner, what does he do?
Oh, perfect.
He doesn't run away.
I don't believe he sees us.
And if we go into the locker.
Perfect.
He leaves us alone.
So if we exit out of the locker, there we go.
He's back again, wandering around the map.
So let me see if I can find a locker somewhere to go hide.
There's one right there.
Oh, we go into our locker.
Perfect.
And there he goes off wandering around our map.
So let's just kind of follow him around and let's see if he disappears once he reaches too many walking
iterations.
So he's continuing to wander around the map.
He has no idea we're behind him.
Oops does appear that we reset his walking iterations, so we're going to have to wait again.
All right.
It's continuing to pathfind around the map.
That's a little spooky, ain't it?
Where'd he go?
Look, you definitely saw us there.
But so far, it looks like our Squidward was working great.
As you can see, he's wandering around the map.
Can't get too close to him or else he'll attack a jump scare us.
And as you can see, he can be quite fast.
So that's a pretty terrifying thing to see chasing after you.
And there he goes off pathfinding again.
Perfect.
So it seems that our AI, Squidward, is working great.
So I believe the next thing we'll have to work on is setting up that spectating system for our players
when we die and get jumpscared by our AI, Squidward.
So I'll go ahead and see you in the next lecture.